package factory

import "fmt"

//
//type Person struct {
//	Name string
//	Age int
//}
//
//func (p Person) Greet()  {
//	fmt.Printf("Hi! My name is %s",p.Name)
//}
//// 简单工厂模式接受一些参数，然后返回Person实例的函数
//func NewPerson(name string,age int) *Person {
//	return &Person{
//		Name: name,
//		Age: age,
//	}
//}

// 抽象工厂模式，返回的是接口而不是结构体
// 通过返回接口，可以在你不公开内部实现的情况下，让调用者使用你提供的各种功能
//type Person interface {
//	Greet()
//}
//
//type person struct {
//	name string
//	age int
//}
//
//func (p person) Greet() {
//	fmt.Printf("Hi! My name is %s",p.name)
//}
//
//func NewPerson(name string,age int) Person {
//	return person{
//		name: name,
//		age: age,
//	}
//}

// 通过返回接口，可以实现多个工厂函数，来返回不同的接口实现
//type Doer interface {
//	Do(req *http.Request) (*http.Response,error)
//}
//
//func NewHTTPClient() Doer {
//	return &http.Client{}
//}
//
//type mockHTTPClient struct {
//
//}
//
//func (*mockHTTPClient) Do(req *http.Request) (*http.Response,error) {
//	res := httptest.NewRecorder()
//	return res.Result(),nil
//}
//
//func NewMockHTTPClient() Doer {
//	return &mockHTTPClient{}
//}
//
//func QueryUser(doer Doer) error {
//	req,err := http.NewRequest("Get","http://iam.api.marmotedu.com:8080/v1/secrets",nil)
//	if err != nil { return err }
//	_, err = doer.Do(req)
//	if err != nil {
//		return err
//	}
//	return nil
//}
//func TestQueryUser(t *testing.T) {
//	doer := NewMockHTTPClient()
//	if err := QueryUser(doer); err != nil {
//		t.Errorf("QueryUser failed, err: %v", err)
//	}
//}

// 在简单工厂模式中，依赖于唯一的工厂对象，如果我们需要实例化一个产品，就要向工厂中传入一个参数，
//// 获取对应的对象；如果要增加一种产品，就要在工厂中创建产品的函数。这会导致耦合性过高，这时我们就可以使用工厂方法模式
//type Person struct {
//	name string
//	age int
//}
//
//func NewPersonFactory(age int) func(name string) Person {
//	return func(name string) Person {
//		return Person{
//			name: name,
//			age: age,
//		}
//	}
//}

// 我们可以使用此功能来创建具有默认年龄的工厂：
//newBaby := NewPersonFactory(1)
//baby := newBaby("join")

// 在项目开发中，我们经常要根据不同的场景，采取不同的措施，也就是不同的策略。比如，假设我们需要对 a、b 这两个整数进行计算，根据条件的不同，需要执行不同的计算方式。我们可以把所有的操作都封装在同一个函数中，然后通过 if ... else ... 的形式来调用不同的计算方式，这种方式称之为硬编码。
//在实际应用中，随着功能和体验的不断增长，我们需要经常添加 / 修改策略，这样就需要不断修改已有代码，不仅会让这个函数越来越难维护，还可能因为修改带来一些 bug。所以为了解耦，需要使用策略模式，定义一些独立的类来封装不同的算法，每一个类封装一个具体的算法（即策略）。
// 策略模式
// 定义一个策略类
//type IStrategy interface {
//	do(int,int) int
//}
//
//// 策略实现：加
//type add struct {
//
//}
//
//func (*add) do(a,b int) int  {
//	return a+b
//}
//
////策略实现：减
//type reduce struct {
//
//}
//
//func (*reduce) do(a,b int) int {
//	return a-b
//}
//
//// 具体策略的执行者
//type Operator struct {
//	strategy IStrategy
//}
//
//// 设置策略
//func (operator *Operator) setStrategy(strategy IStrategy) {
//	operator.strategy = strategy
//}
////调用策略中的方法
//func (operator *Operator) calculate(a,b int) int {
//	return operator.strategy.do(a,b)
//}
//
//// 在上述代码中，我们定义了策略接口IStrategy,还定义了add 和 reduce 两种策略。最后定义了一个策略执行者，可以设置不同的
//// 策略，并执行
//func TestStrategy(t *testing.T)  {
//	operator := Operator{}
//	operator.setStrategy(&add{})
//	result := operator.calculate(1,2)
//	fmt.Println("add:",result)
//
//	operator.setStrategy(&reduce{})
//	result = operator.calculate(2,1)
//	fmt.Println("reduce:",result)
//}

//模板模式
//模板模式 (Template Pattern) 定义一个操作中算法的骨架，而将一些步骤延迟到子类中。这种方法让子类在不改变一个算法结构的情况下，就能重新定义该算法的某些特定步骤。简单来说，模板模式就是将一个类中能够公共使用的方法放置在抽象类中实现，将不能公共使用的方法作为抽象方法，强制子类去实现，这样就做到了将一个类作为一个模板，让开发者去填充需要填充的地方。
//
//type Cooker interface {
//	fire()
//	cooke()
//	outfire()
//}
//
//// 类似于一个抽象类
//type CookMenu struct {
//
//}
//
//func (CookMenu) fire() {
//	fmt.Println("开火")
//}
//
//// 做菜，交给具体的子类实现
//func (CookMenu) cooke() {
//
//}
//
//func (CookMenu) outfire() {
//	fmt.Println("关火")
//}
//
//// 封装具体步骤
//func doCook(cook Cooker)  {
//	cook.fire()
//	cook.cooke()
//	cook.outfire()
//}
//
//type XiHongShi struct {
//	CookMenu
//}
//
//func (*XiHongShi) cooke() {
//	fmt.Println("做西红柿")
//}
//
//type ChaoJiDan struct {
//	CookMenu
//}
//
//func (ChaoJiDan) cooke() {
//	fmt.Println("做炒鸡蛋")
//}
//
//func TestTemplate(t *testing.T)  {
//	// 做西红柿
//	xihongshi := &XiHongShi{}
//	doCook(xihongshi)
//
//	fmt.Println("做另外一道菜")
//
//	//做炒鸡蛋
//	chaojidan := &ChaoJiDan{}
//	doCook(chaojidan)
//}

// 代理模式
//type Seller interface {
//	sell(name string)
//}
//
//// 火车站
//type Station struct {
//	stock int  // 库存
//}
//
//func (station *Station) sell(name string) {
//	if station.stock > 0{
//		station.stock--
//		fmt.Printf("代理点中:%s买了一张票，剩余:%d \n",name,station.stock)
//	} else {
//		fmt.Println("票已售空")
//	}
//}
//
//// 火车代理点
//type StationProxy struct {
//	station *Station
//}
//
//func (proxy *StationProxy) sell(name string) {
//	if proxy.station.stock > 0{
//		proxy.station.stock--
//		fmt.Printf("代理点中:%s买了一张票，剩余:%d \n",name,proxy.station.stock)
//	} else {
//		fmt.Println("票已售空")
//	}
//}

// 选项模式
//const (
//	defaultTimeout = 10
//	defaultCaching = false
//	)
//type Connection struct {
//	addr string
//	cache bool
//	timeout time.Duration
//}
//// NewConnect creates a connection.
//func NewConnect(addr string) (*Connection, error) {
//	return &Connection{ addr: addr, cache: defaultCaching, timeout: defaultTimeout, }, nil
//}
//// NewConnectWithOptions creates a connection with options.
//func NewConnectWithOptions(addr string, cache bool, timeout time.Duration) (*Connection, error) { return &Connection{ addr: addr, cache: cache, timeout: timeout, }, nil}

//type Connection struct {
//	addr string
//	cache bool
//	timeout time.Duration
//}
//
//const (
//	defaultTimeout = 10
//	defaultCaching = false
//)
//
//type options struct {
//	timeout time.Duration
//	caching bool
//}
//
//type Option interface {
//	apply(*options)
//}
//
//type optionFunc func(*options)
//
//func (f optionFunc) apply(o *options) {
//	f(o)
//}
//
//func WithTimeout(t time.Duration) Option {
//	return optionFunc(func(o *options) {
//		o.timeout = t
//	})
//}
//
//func WithCaching(cache bool) Option {
//	return optionFunc(func(o *options) {
//		o.caching = cache
//	})
//}
//
//func NewConnect(addr string,opts ...Option)(*Connection,error)  {
//	options := options{
//		timeout: defaultTimeout,
//		caching: defaultCaching,
//	}
//	for _,o := range opts{
//		o.apply(&options)
//	}
//	return &Connection{
//		addr: addr,
//		cache: options.caching,
//		timeout: options.timeout,
//	},nil
//}

type Bird struct {
	Type string
}

func (bird *Bird) Class() string {
	return bird.Type
}

type Birds interface {
	Name() string
	Class() string
}

type Canary struct {
	Bird
	name string
}

func (c *Canary) Name() string {
	return c.name
}

type Crow struct {
	Bird
	name string
}

func (c *Crow) Name() string {
	return c.name
}

func NewCrow(name string) *Crow {
	return &Crow{
		Bird: Bird{
			Type: "Crow",
		},
		name: name,
	}
}

func NewCanary(name string) *Canary {
	return &Canary{
		Bird: Bird{
			Type: "Canary",
		},
		name: name,
	}
}

func BirdINfo(birds Birds) {
	fmt.Printf("I am %s,I belong to %s  ", birds.Name(), birds.Class())
}
